package com.example.blogserver.service.Impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.blogserver.a_utils.MqConstants;
import com.example.blogserver.mapper.ArticleMapper;
import com.example.blogserver.mapper.SensitiveWordMapper;
import com.example.blogserver.mapper.UserMapper;
import com.example.blogserver.pojo.Article;
import com.example.blogserver.pojo.SensitiveWord;
import com.example.blogserver.pojo.User;
import com.example.blogserver.service.ListService;
import com.example.blogserver.a_utils.Constant;
import com.example.blogserver.a_utils.GetBean;
import com.example.blogserver.a_utils.ResponseBodyMessage;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.MutableDataSet;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Whitelist;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@SuppressWarnings({"all"})
@Slf4j
@Service
public class ListServiceImpl implements ListService {
    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private UserMapper userMapper;

    /**
     * 获取Redis操作对象
     */
    private static RedisTemplate redisTemplate = (RedisTemplate) GetBean.getBean("redisTemplate");

    /**
     * ZSet当中的 key（获赞量）
     */
    public static final String GOOD_RANK = "good_rank";

    /**
     * ZSet当中的 key（访问量)
     */
    public static final String READ_RANK = "read_rank";

    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private SensitiveWordMapper sensitiveWordMapper;

    /**
     * 获取用户信息
     */
    @Override
    public ResponseBodyMessage<User> queryByAccount(HttpServletRequest request) {
        ResponseBodyMessage responseBodyMessage = new ResponseBodyMessage();
        //获取Session当中存储的用户的账号（邮箱或者账号）
        String account = (String) request.getSession(false).getAttribute(Constant.USERINFO_SESSION_KEY);
        User user = userMapper.queryUser(account);
        //判断是否为新用户
        String name = user.getUsername();
        if (name.equals("新手用户")) {
            //说明为新用户
            responseBodyMessage.setStatus(-1);
            responseBodyMessage.setMessage("检测到当前账号为新用户，请完善信息后使用~");
        } else {
            responseBodyMessage.setStatus(1);
            responseBodyMessage.setMessage("非新用户~");
        }
        user.setExamineSum(articleMapper.getExamineSumByUserInfo());
        user.setFailSum(articleMapper.getFailSumByUserInfo());
        responseBodyMessage.setData(user);
        return responseBodyMessage;
    }

    /**
     * 获取全部博客
     */
    @Override
    public ResponseBodyMessage<List<Article>> getAllBlog(HttpServletRequest request) {
        ResponseBodyMessage responseBodyMessage = new ResponseBodyMessage();
        String check = request.getParameter("userId");
        if (check != null) {
            //表示获取单个用户的文章列表
            ResponseBodyMessage<List<Article>> result = getArticlesByAccount(request);
            return result;
        } else {
            User user = queryByAccount(request).getData();
            int articleStatus = 1;
            if (user.getIdentity() == 1 || user.getIdentity() == 2) {
                //说明为管理员身份，获取待审核文章
                articleStatus = 0;
            }
            List<Article> articles = articleMapper.getAllBlog(articleStatus);
            for (Article article : articles) {
                String convert = ListServiceImpl.convert(article.getContent());
                String convert2 = convert2(convert);
                if (convert2.length() > 250) {
                    convert2 = convert2.substring(0, 250);
                    convert2 = convert2 + "......";
                }
                article.setContent(convert2);
            }
            if (articles.size() == 0) {
                responseBodyMessage.setStatus(-1);
                responseBodyMessage.setMessage("当前还没有用户发布过博客~");
                responseBodyMessage.setData(null);
            } else {
                responseBodyMessage.setStatus(1);
                responseBodyMessage.setMessage("获取成功~");
                responseBodyMessage.setData(articles);
            }
        }
        return responseBodyMessage;
    }

    /**
     * 根据关键字查询博客
     */
    @Override
    public ResponseBodyMessage<List<Article>> getArticleByWord(String word, String queryRules) {
        ResponseBodyMessage responseBodyMessage = new ResponseBodyMessage();
        if (word == null || word.equals("")) {
            responseBodyMessage.setStatus(0);
            //未输入关键字就获取全部博客
            responseBodyMessage.setMessage("由于您未输入查询关键字，因此返回全部博客~");
            responseBodyMessage.setData(null);
        } else {
            List<Article> articles = articleMapper.getArticlesByWord(word);
            if (articles.size() == 0) {
                responseBodyMessage.setStatus(-1);
                responseBodyMessage.setMessage("未查询到与您输入的信息相关的博客~");
                responseBodyMessage.setData(null);
            } else {
                if (queryRules != null && queryRules.contains("获赞量")) {
                    //根据获赞量降序排列
                    articles = rankByGood(articles);
                } else if (queryRules != null && queryRules.contains("访问量")) {
                    //根据访问量降序排列
                    articles = rankByRead(articles);
                }
                //数据库默认查询规则（最新发布时间）
                for (Article article : articles) {
                    String convert = ListServiceImpl.convert(article.getContent());
                    String convert2 = convert2(convert);
                    if (convert2.length() > 250) {
                        convert2 = convert2.substring(0, 250);
                        convert2 = convert2 + "......";
                    }
                    article.setContent(convert2);
                }
                responseBodyMessage.setStatus(1);
                responseBodyMessage.setMessage("查询成功~");
                responseBodyMessage.setData(articles);
            }
        }
        return responseBodyMessage;
    }

    /**
     * 根据访问量降序排列
     */
    private List<Article> rankByRead(List<Article> list) {
        Set<ZSetOperations.TypedTuple<Article>> tuples = new HashSet<>();
        if (CollectionUtils.isNotEmpty(list)) {
            for (Article article : list) {
                DefaultTypedTuple<Article> tuple = new DefaultTypedTuple<>(article, article.getConsult().doubleValue());
                tuples.add(tuple);
            }
        }
        redisTemplate.opsForZSet().add(READ_RANK, tuples);
        redisTemplate.expire(READ_RANK, 1, TimeUnit.SECONDS);//1秒过期
        Set rangeWithRead = redisTemplate.opsForZSet().reverseRangeByScore(READ_RANK, 0, 10_0000_0000);
        List<Article> result = new ArrayList<>(rangeWithRead);
        return result;
    }

    /**
     * 根据获赞量降序排列
     */
    private List<Article> rankByGood(List<Article> list) {
        Set<ZSetOperations.TypedTuple<Article>> tuples = new HashSet<>();
        if (CollectionUtils.isNotEmpty(list)) {
            for (Article article : list) {
                DefaultTypedTuple<Article> tuple = new DefaultTypedTuple<>(article, article.getPraise().doubleValue());
                tuples.add(tuple);
            }
        }
        redisTemplate.opsForZSet().add(GOOD_RANK, tuples);
        redisTemplate.expire(GOOD_RANK, 1, TimeUnit.SECONDS);//1秒过期
        Set rangeWithGood = redisTemplate.opsForZSet().reverseRangeByScore(GOOD_RANK, 0, 10_0000_0000);
        List<Article> result = new ArrayList<>(rangeWithGood);
        return result;
    }


    /**
     * 获取用户所发布的文章
     */
    @Override
    public ResponseBodyMessage<List<Article>> getArticlesByAccount(HttpServletRequest request) {
        String account = "";
        if (request.getParameter("userId") != null) {
            account = request.getParameter("userId");
        } else {
            //获取账户信息
            String ID = String.valueOf(request.getSession(false).getAttribute(Constant.USERINFO_SESSION_KEY));
            if (ID.indexOf('.') == -1) {
                //表示不包含. 使用的是id登录
                account = ID;
            } else {
                //表示使用的是邮箱登录
                //根据邮箱获取userid
                User user = getUserByEamil(ID);
                account = user.getUserid();
            }
        }
        String status = new String();
        if (request.getParameter("status") == null) {
            status = "1";
        } else {
            status = request.getParameter("status");
        }
        User user = userMapper.queryUser(account);
        List<Article> ret = new ArrayList<>();
        if (user.getIdentity() == 1 || user.getIdentity() == 2) {
            ret = articleMapper.getAllBlog(Integer.parseInt(status));
        } else {
            ret = articleMapper.getArticlesByAccount(account, Integer.parseInt(status));
        }
        for (Article article : ret) {
            String convert = ListServiceImpl.convert(article.getContent());
            String convert2 = convert2(convert);
            if (convert2.length() > 250) {
                convert2 = convert2.substring(0, 250);
                convert2 = convert2 + "......";
            }
            article.setContent(convert2);
        }
        ResponseBodyMessage<List<Article>> result = new ResponseBodyMessage<>();
        if (user.getIdentity() == 1 || user.getIdentity() == 2) {
            if (ret.size() == 0) {
                result.setStatus(-1);
                result.setMessage("当前没有任何文章需要审核~");
                result.setData(null);
            } else {
                result.setStatus(1);
                result.setMessage("获取成功~~");
                result.setData(ret);
            }
        } else {
            if (ret.size() == 0) {
                result.setStatus(-1);
                result.setMessage("您当前还未发布过文章，请发布后再来查看~");
                result.setData(null);
            } else {
                result.setStatus(1);
                result.setMessage("获取成功~~");
                result.setData(ret);
            }
        }
        return result;
    }

    /**
     * 根据邮箱账号获取信息
     */
    @Override
    public User getUserByEamil(String id) {
        User user = userMapper.queryUser(id);
        return user;
    }

    /**
     * 一键审核列表中含有敏感词的文章
     */
    @Override
    public ResponseBodyMessage<String> checkArticlesByIk(HttpServletRequest request) {
        ResponseBodyMessage responseBodyMessage = new ResponseBodyMessage();
        String account = (String) request.getSession(false).getAttribute(Constant.USERINFO_SESSION_KEY);
        User user = userMapper.queryUser(account);
        if (user.getIdentity() == 1 || user.getIdentity() == 2) {
            SearchResponse response = getSearchResponse();
            Set<Article> articles = handleResponse(response);
            if (articles.size() > 0) {
                for (Article article : articles) {
                    articleMapper.updateArticleStatus(String.valueOf(article.getId()), 2);
                    //2.修改用户的审核中状态数和审核不通过状态数 examine-1 fail+1
                    userMapper.updateUserSumExamine(article.getUserId());
                    userMapper.updateUserSumFail(article.getUserId());
                    rabbitTemplate.convertAndSend(MqConstants.ARTICLE_EXCHANGE, MqConstants.ARTICLE_DELETE_KEY, article.getId());
                    responseBodyMessage.setStatus(1);
                    responseBodyMessage.setData("已经将所有含有敏感词的位置审核为失败");
                    responseBodyMessage.setMessage("已经将所有含有敏感词的位置审核为失败");
                }
            } else if (articles.size() == 0) {
                responseBodyMessage.setStatus(-1);
                responseBodyMessage.setData("未发现有敏感词的文章");
                responseBodyMessage.setMessage("未发现有敏感词的文章");
            }
        }
        return responseBodyMessage;
    }

    private static Set<Article> handleResponse(SearchResponse response) {
        // 4. 解析响应
        SearchHits searchHits = response.getHits();
        // 4.1 获取总条数
        Long total = searchHits.getTotalHits().value;
        // 4.2 文档数组
        SearchHit[] hits = searchHits.getHits();
        List<Article> articles = new ArrayList<>();
        // 4.3 遍历
        for (SearchHit hit : hits) {
            // 获取文档source
            String json = hit.getSourceAsString();
            // 反序列化
            Article article = JSON.parseObject(json, Article.class);
            articles.add(article);
        }
        Set<Article> deduplicatedArticles = new HashSet<>(articles);
        return deduplicatedArticles;
    }

    private SearchResponse getSearchResponse() {
        try {
            // 1. 准备request
            SearchRequest searchRequest = new SearchRequest("article");
            List<String> sensitiveWords = new ArrayList<>();
            // 查询敏感词库
            QueryWrapper<SensitiveWord> sensitiveWordQueryWrapper = new QueryWrapper<>();
            List<SensitiveWord> sensitiveWordsBySQL = sensitiveWordMapper.selectList(sensitiveWordQueryWrapper);
            for (SensitiveWord word : sensitiveWordsBySQL) {
                sensitiveWords.add(word.getWord());
            }
            // 2. 准备SDL
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            for (String word : sensitiveWords) {
                boolQuery.should(QueryBuilders.matchQuery("all", word));
            }
            searchRequest.source().query(boolQuery);
            // 3. 发送请求
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            return response;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * markdown文本转html文本
     */
    public static String convert(String md) {
        MutableDataSet options = new MutableDataSet();
        Parser parser = Parser.builder(options).build();
        HtmlRenderer renderer = HtmlRenderer.builder(options).build();
        Node document = parser.parse(md);
        String html = renderer.render(document);
        return html;
    }

    /**
     * 将html文本转纯文本
     */
    public static String convert2(String html) {
        if (StringUtils.isEmpty(html)) {
            return "";
        }
        Document document = Jsoup.parse(html);
        Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false);
        document.outputSettings(outputSettings);
        document.select("br").append("\\n");
        document.select("p").prepend("\\n");
        document.select("p").append("\\n");
        String newHtml = document.html().replaceAll("\\\\n", "\n");
        String plainText = Jsoup.clean(newHtml, "", Whitelist.none(), outputSettings);
        String result = StringEscapeUtils.unescapeHtml(plainText.trim());
        return result;
    }
}
